-
-
Notifications
You must be signed in to change notification settings - Fork 41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds within
and eval_code
to teal_data_module
#983
Conversation
within.teal_data_module <- function(data, expr, ...) { | ||
expr <- substitute(expr) | ||
extras <- list(...) | ||
|
||
# Add braces for consistency. | ||
if (!identical(as.list(expr)[[1L]], as.symbol("{"))) { | ||
expr <- call("{", expr) | ||
} | ||
|
||
calls <- as.list(expr)[-1] | ||
|
||
# Inject extra values into expressions. | ||
calls <- lapply(calls, function(x) do.call(substitute, list(x, env = extras))) | ||
|
||
eval_code(object = data, code = as.expression(calls)) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The body is a copy from teal.code:::within.qenv
, it just prepares the arguments to call eval_code()
(data
is unchanged)
So in practice it could be replaced with code below (reducing duplicated code accross {teal.*}
):
Thoughts?
within.teal_data_module <- function(data, expr, ...) { | |
expr <- substitute(expr) | |
extras <- list(...) | |
# Add braces for consistency. | |
if (!identical(as.list(expr)[[1L]], as.symbol("{"))) { | |
expr <- call("{", expr) | |
} | |
calls <- as.list(expr)[-1] | |
# Inject extra values into expressions. | |
calls <- lapply(calls, function(x) do.call(substitute, list(x, env = extras))) | |
eval_code(object = data, code = as.expression(calls)) | |
} | |
within.teal_data_module <- function(data, expr, ...) { | |
within.qenv <- getFromNamespace("within.qenv", "teal.code") | |
within.qenv(data, expr, ...) | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- I think
getS3method("within", "qenv", envir = as.environment(getNamespace("teal.code")))
would be a better choice. - It shouldn't be necessary anyway because
teal.code
must be installed whenteal
is. - At the moment
within
only processes the expression and callseval_code
. In this context this is a valid simplification. I'm not sure, though 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- I think getS3method("within", "qenv", envir = as.environment(getNamespace("teal.code"))) would be a better choice.
- It shouldn't be necessary anyway because teal.code must be installed when teal is.
That's better! Is the envir
parameter required? (as you said, teal.code
is imported and it seems to find the within)
- At the moment within only processes the expression and calls eval_code. In this context this is a valid simplification. I'm not sure, though 🤔
@vedhav brough a smilar concern, I'm on the fence as I prefer to minimize repetitive code, but I don't think this solution is "best practice" (calling within.qenv
with a different data object seems like a red flag).
I would prefer if there was within.default
(non-exported) or .generic_within
in {teal.code}
that was the backbone of both of those calls and still allow for deviations in the future of within.qenv
that wouldn't affect within.teal_data_module
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's better! Is the
envir
parameter required?
It isn't but just in case there is a different within.qenv
defined somewhere...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer if there was
within.default
The within
generic is a base
function so defining a default
that suits our particular classes would cause a mess.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The within generic is a base function so defining a default that suits our particular classes would cause a mess.
Yup, so a .generic_within
function would be best if we wanted to go this route.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using the .default
was a suggestion if it didn't interfere with base::within
(by not exporting it, but even then it might be "dangerous")
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, but S3 methods must be exported 😉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gogonzo would you like to pitch in?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One interesting pattern that I have found when working on the verdepcheck
is to make a simple (i.e. non S3 / non S4 etc.) (implementation) function for each method and these methods are essentially a one-liner calling those functions. Please find an example in R6 context: https://github.com/r-lib/pkgdepends/blob/main/R/pkg-plan.R - all public methods are calling private (implementation) functions.
Then you can re-use those implementation functions from multiple classes but there are some obvious drawbacks though such as number of objects in a package, documentation, maintenance, debugging etc.
This however is not addressing an exporting / importing issue mentioned here 😈
Code Coverage Summary
Diff against main
Results for commit: b1625ba Minimum allowed coverage is ♻️ This comment has been updated with latest results |
Unit Test Performance Difference
Additional test case details
Results for commit 9163527 ♻️ This comment has been updated with latest results. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are missing tests and poc for teal application. There are already tests on data
(teal_data_module) output. I wonder what would be the output of eval_code(teal_data_module, code)
if there is an error in teal_data_module
or in the code
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nothing to add in my opinion. Good job!
within.teal_data_module <- function(data, expr, ...) { | ||
expr <- substitute(expr) | ||
extras <- list(...) | ||
|
||
# Add braces for consistency. | ||
if (!identical(as.list(expr)[[1L]], as.symbol("{"))) { | ||
expr <- call("{", expr) | ||
} | ||
|
||
calls <- as.list(expr)[-1] | ||
|
||
# Inject extra values into expressions. | ||
calls <- lapply(calls, function(x) do.call(substitute, list(x, env = extras))) | ||
|
||
eval_code(object = data, code = as.expression(calls)) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gogonzo would you like to pitch in?
R/teal_data_module-eval_code.R
Outdated
sep = "\n", | ||
"Error when executing `teal_data_module`:", | ||
paste0( | ||
"It must always return a reactive with `teal_data`, it returns object of class(es): ", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As in srv_teal_with_splash
, I believe this error message is for the app dev, not for the app user. It should shop up in the console, not in the app.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It depends. Let's say we have teal_data_module that reads csv files only and an app user select e.g. pdf file then we probably need to expose error message for app user.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then it is up to the module developer to validate the file choice.
Co-authored-by: Aleksander Chlebowski <[email protected]> Signed-off-by: André Veríssimo <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please address comments and fix R CMD check
Pull Request
Fixes #nnn
Changes description
within
andeval_code
toteal_data_module
data-as-shiny-module
vignette{teal.code}
fromSuggests
toImports